import asyncio
import base64
import hashlib
import os
import tempfile
from typing import Optional
import threading

import requests
from wechatpy import WeChatClient

from common.cache import media_cache


def verify_signature(signature: str, timestamp: str, nonce: str, token: str) -> bool:
    """验证微信服务器签名"""
    temp_list = [token, timestamp, nonce]
    temp_list.sort()
    temp_str = "".join(temp_list)
    hash_obj = hashlib.sha1(temp_str.encode('utf-8'))
    hash_str = hash_obj.hexdigest()
    return hash_str == signature


class WxServer:
    _instance = None
    _lock = threading.Lock()

    def __init__(self, app_id: str, app_secret: str):
        self.app_id = app_id
        self.app_secret = app_secret
        self.client = WeChatClient(app_id, app_secret)

    @classmethod
    def get_instance(cls, app_id: str, app_secret: str) -> 'WxServer':
        """获取WxServer的单例实例"""
        if not cls._instance:
            with cls._lock:
                if not cls._instance:
                    cls._instance = cls(app_id, app_secret)
        return cls._instance

    def get_access_token(self) -> str:
        """获取微信接口调用凭证"""
        return self.client.access_token

    def send_message(self, to_user: str, msg_type: str, content: dict) -> str:
        """发送消息到微信用户"""
        try:
            if msg_type == "text":
                self.client.message.send_text(to_user, content['content'])
                
            elif msg_type == "image":
                media_id = self.upload_media("image", content['content'])
                self.client.message.send_image(to_user, media_id)
                
            elif msg_type == "voice":
                media_id = self.upload_media("voice", content['content'])
                self.client.message.send_voice(to_user, media_id)
                
            elif msg_type == "video":
                media_id = self.upload_media("video", content['content'])
                self.client.message.send_video(to_user, media_id, 
                                            title="AI生成视频", 
                                            description="基于您的输入生成的视频")
            
            return "success"
            
        except Exception as e:
            print(f"发送消息失败: {str(e)}")
            return "success"  # 即使发送失败也返回success避免微信服务器重试

    def upload_media(self, media_type: str, file_content: bytes) -> str:
        """上传临时素材到微信服务器"""
        temp_file = None
        try:
            # 根据媒体类型确定文件扩展名
            ext_map = {
                "image": ".jpg",
                "voice": ".mp3", 
                "video": ".mp4"
            }
            
            if media_type not in ext_map:
                raise ValueError(f"不支持的媒体类型: {media_type}")
                
            # 使用with语句确保文件正确关闭
            with tempfile.NamedTemporaryFile(suffix=ext_map[media_type], delete=False) as temp_file:
                temp_file.write(file_content)
                temp_file.flush()
                temp_path = temp_file.name
            
            # 使用客户端的media上传方法
            with open(temp_path, 'rb') as f:
                response = self.client.media.upload(media_type, f)
            
            # 删除临时文件
            if os.path.exists(temp_path):
                try:
                    os.unlink(temp_path)
                except Exception as e:
                    print(f"删除临时文件失败: {str(e)}")
                
            return response['media_id']
            
        except Exception as e:
            # 确保在发生异常时也能清理临时文件
            if temp_file and os.path.exists(temp_file.name):
                try:
                    os.unlink(temp_file.name)
                except Exception as cleanup_error:
                    print(f"清理临时文件失败: {str(cleanup_error)}")
            print(f"上传媒体文件失败: {str(e)}")
            raise

    def download_media(self, media_id: str) -> Optional[bytes]:
        """从微信服务器下载媒体文件"""
        try:
            # 检查缓存
            if cached_path := media_cache.get(media_id):
                if os.path.exists(cached_path):
                    with open(cached_path, 'rb') as f:
                        return f.read()

            # 如果缓存中没有,从微信服务器下载
            response = self.client.media.download(media_id)
            if response and response.content:
                return response.content
            return None
        except Exception as e:
            print(f"下载媒体文件失败: {str(e)}")
            return None

    async def recognize_voice(self, media_id: str, lang: str = "zh_CN") -> Optional[str]:
        """调用微信语音识别接口"""
        access_token = self.get_access_token()
        
        # 提交语音进行识别
        submit_url = f"https://api.weixin.qq.com/cgi-bin/media/voice/addvoicetorecofortext?access_token={access_token}&format=mp3&voice_id={media_id}&lang={lang}"
        
        try:
            response = requests.post(submit_url)
            response.raise_for_status()
            result = response.json()
            
            if result.get("errcode") != 0:
                raise Exception(f"提交语音识别失败: {result.get('errmsg')}")
            
            # 等待并获取识别结果
            query_url = f"https://api.weixin.qq.com/cgi-bin/media/voice/queryrecoresultfortext?access_token={access_token}&voice_id={media_id}&lang={lang}"
            
            max_retries = 5
            for _ in range(max_retries):
                response = requests.post(query_url)
                response.raise_for_status()
                result = response.json()
                
                if "result" in result:
                    return result["result"]
                
                await asyncio.sleep(2)  # 等待2秒后重试
                
            raise Exception("语音识别超时")
            
        except Exception as e:
            print(f"语音识别失败: {str(e)}")
            return None 